// YUVAnalyzerDlg.cpp : implementation file
//

#include "stdafx.h"
#include "YUVAnalyzer.h"
#include "YUVAnalyzerDlg.h"
#include "SettingDlg.h"
#include "math.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()











/////////////////////////////////////////////////////////////////////////////
// CYUVAnalyzerDlg dialog

CYUVAnalyzerDlg::CYUVAnalyzerDlg(CWnd* pParent /*=NULL*/)
	: cdxCSizingDialog(CYUVAnalyzerDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CYUVAnalyzerDlg)
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	bPlay = FALSE;
	bDual = FALSE;
	bYUV = YUV;

	m_playbackAA.SetColorful(bYUV);
	m_playbackA.SetColorful(bYUV);
	m_playbackB.SetColorful(bYUV);
	iWidth = 352; iHeight = 288;
	iCurrFrameNumber = 0;
	iTotalFrameNumberA = 0;
	iTotalFrameNumberB = 0;
	iMaxFrameNumber = 1;
	fps = 30;

	return;
}

void CYUVAnalyzerDlg::DoDataExchange(CDataExchange* pDX)
{
	cdxCSizingDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CYUVAnalyzerDlg)
	DDX_Control(pDX, IDC_PSNR, m_psnr);
	DDX_Control(pDX, IDC_GRID, m_grid);
	DDX_Control(pDX, IDC_YUV, m_YUV);
	DDX_Control(pDX, IDC_ORIG_SIZE, m_orig_size);
	DDX_Control(pDX, IDC_PLAYBACK_AA, m_playbackAA);
	DDX_Control(pDX, IDC_MV, m_bMV);
	DDX_Control(pDX, IDC_PLAYBACK_B, m_playbackB);
	DDX_Control(pDX, IDC_PLAYBACK_A, m_playbackA);
	DDX_Control(pDX, IDC_SINGLE_DUAL, m_single_dual);
	DDX_Control(pDX, IDC_SETTING, m_setting);
	DDX_Control(pDX, IDC_PLAY, m_play_pause);
	DDX_Control(pDX, IDC_FRAME_NUM, m_frame_num);
	DDX_Control(pDX, IDC_PROGRESS, m_progress);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CYUVAnalyzerDlg, cdxCSizingDialog)
	//{{AFX_MSG_MAP(CYUVAnalyzerDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_CLOSE()
	ON_BN_CLICKED(IDC_SINGLE_DUAL, OnSingleDual)
	ON_BN_CLICKED(IDC_PLAY, OnPlay)
	ON_WM_TIMER()
	ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_PROGRESS, OnReleasedcaptureProgress)
	ON_BN_CLICKED(IDC_SETTING, OnSetting)
	ON_BN_CLICKED(IDC_MV, OnMV)
	ON_BN_CLICKED(IDC_ORIG_SIZE, OnOrigSize)
	ON_BN_CLICKED(IDC_YUV, OnYUV)
	ON_BN_CLICKED(IDC_GRID, OnGrid)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CYUVAnalyzerDlg message handlers

BOOL CYUVAnalyzerDlg::OnInitDialog()
{
	cdxCSizingDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here
	AddSzControlEx(m_playbackAA, 0, 100, 0, 100);
	m_playbackAA.ShowWindow(SW_SHOWNORMAL);
	AddSzControlEx(m_playbackA, 0, 50, 0, 100);
	AddSzControlEx(m_playbackB, 50, 100, 0, 100);
	m_playbackA.ShowWindow(SW_HIDE);
	m_playbackB.ShowWindow(SW_HIDE);
	AddSzYControl(m_single_dual, mdRepos);	m_single_dual.SetWindowText("1=>2");
	AddSzYControl(m_play_pause, mdRepos);	m_play_pause.SetWindowText("GO");
	AddSzYControl(m_YUV, mdRepos);	m_YUV.SetWindowText("YUV");
	AddSzControl(m_frame_num, mdRepos, mdRepos);
	AddSzYControl(m_psnr, mdRepos); m_psnr.ShowWindow(SW_HIDE);
	AddSzYControl(m_grid, mdRepos);	m_grid.SetCheck(BST_UNCHECKED);
	AddSzYControl(m_bMV, mdRepos);
	AddSzYControl(m_setting, mdRepos);
	AddSzControl(m_progress, mdResize, mdRepos);	m_progress.SetRange(0, iMaxFrameNumber-1);
	AddSzYControl(m_orig_size, mdRepos);

	//CWnd playwnd;
	//RECT r={0, 0, 10, 10};
	//playwnd.Create(NULL, "hello", WS_CHILD, &r, 
	
	RestoreWindowPosition(_T("Main\\Window"));

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CYUVAnalyzerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		cdxCSizingDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CYUVAnalyzerDlg::OnPaint() 
{
	CPaintDC dc(this); // device context for painting

	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		cdxCSizingDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CYUVAnalyzerDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CYUVAnalyzerDlg::OnClose() 
{
	// TODO: Add your message handler code here and/or call default
	StoreWindowPosition(CString(_T("Main\\Window")));

	cdxCSizingDialog::OnClose();
}

void CYUVAnalyzerDlg::OpenFileA() 
{
	// TODO: Add your control notification handler code here

	// get pathname & filename
	m_playbackA.SetPathName(sFileNameA);
	m_playbackAA.SetPathName(sFileNameA);
	////AfxMessageBox(sFileNameA, MB_OK);

	CFile *pFile = new CFile();
	if(!pFile->Open(sFileNameA, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone )) 
	{
		//AfxMessageBox("Can't open input file");
		delete pFile;
		return;
	}
	iTotalFrameNumberA = pFile->GetLength() / (iWidth*iHeight*3/2);
	if (iTotalFrameNumberA > iTotalFrameNumberB)
		iMaxFrameNumber = iTotalFrameNumberA;
	iCurrFrameNumber = 0;
	pFile->Close();
	delete pFile;

	Restart();
}

void CYUVAnalyzerDlg::ShowPSNR()
{
	char psnr_res[64];

	if (bDual) {
		switch (bYUV) {
		case YUV:
		case YY:
			sprintf(psnr_res, "PSNR-Y\n%6.3f", CalcPSNR(m_playbackA.Y, m_playbackB.Y, bYUV));
			break;
		case UU:
			sprintf(psnr_res, "PSNR-U\n%6.3f", CalcPSNR(m_playbackA.Cb, m_playbackB.Cb, bYUV));
			break;
		case VV:
			sprintf(psnr_res, "PSNR-V\n%6.3f", CalcPSNR(m_playbackA.Cr, m_playbackB.Cr, bYUV));
			break;
		}
		m_psnr.SetWindowText(psnr_res);
	}
}

void CYUVAnalyzerDlg::OnSingleDual() 
{
	// TODO: Add your control notification handler code here
	if (bDual == TRUE) {
		bDual = FALSE;
		m_single_dual.SetWindowText("1=>2");
		m_playbackA.ShowWindow(SW_HIDE);
		m_playbackB.ShowWindow(SW_HIDE);
		m_playbackAA.ShowWindow(SW_SHOWNORMAL);
		m_psnr.ShowWindow(SW_HIDE);
	} else {
		bDual = TRUE;
		m_single_dual.SetWindowText("2=>1");
		m_playbackA.ShowWindow(SW_SHOWNORMAL);
		m_playbackB.ShowWindow(SW_SHOWNORMAL);
		m_playbackAA.ShowWindow(SW_HIDE);
		m_psnr.ShowWindow(SW_SHOWNORMAL);
	}
}

void CYUVAnalyzerDlg::OnNext() 
{
	// TODO: Add your control notification handler code here
	iCurrFrameNumber++;
	if (iCurrFrameNumber>=iMaxFrameNumber) iCurrFrameNumber = 0;

	char sFrameNumber[16];
	sprintf( sFrameNumber, "%d/%d", iCurrFrameNumber+1, iMaxFrameNumber );
	m_frame_num.SetWindowText(sFrameNumber);
	ShowPSNR();
	m_progress.SetPos(iCurrFrameNumber);

	if (iCurrFrameNumber<iTotalFrameNumberA) {
		m_playbackA.NextFrame();
		m_playbackAA.NextFrame();
	}
	if (iCurrFrameNumber<iTotalFrameNumberB)
		m_playbackB.NextFrame();

	Invalidate(TRUE);
}

void CYUVAnalyzerDlg::OnPrev() 
{
	// TODO: Add your control notification handler code here
	iCurrFrameNumber--;
	if (iCurrFrameNumber<0) iCurrFrameNumber = iMaxFrameNumber-1;

	char sFrameNumber[16];
	sprintf( sFrameNumber, "%d/%d", iCurrFrameNumber+1, iMaxFrameNumber );
	m_frame_num.SetWindowText(sFrameNumber);
	ShowPSNR();
	m_progress.SetPos(iCurrFrameNumber);

	if (iCurrFrameNumber<=iTotalFrameNumberA-1) {
		m_playbackA.PrevFrame();
		m_playbackAA.PrevFrame();
	}
	if (iCurrFrameNumber<=iTotalFrameNumberB-1)
		m_playbackB.PrevFrame();

	Invalidate(TRUE);
}

void CYUVAnalyzerDlg::OnReleasedcaptureProgress(NMHDR* pNMHDR, LRESULT* pResult) 
{
	// TODO: Add your control notification handler code here
	iCurrFrameNumber = m_progress.GetPos();

	char sFrameNumber[16];
	sprintf( sFrameNumber, "%d/%d", iCurrFrameNumber+1, iMaxFrameNumber );
	m_frame_num.SetWindowText(sFrameNumber);
	ShowPSNR();

	m_playbackA.GoToFrame(iCurrFrameNumber);
	m_playbackAA.GoToFrame(iCurrFrameNumber);
	m_playbackB.GoToFrame(iCurrFrameNumber);

	Invalidate(TRUE);

	*pResult = 0;
}

void CYUVAnalyzerDlg::OnPlay() 
{
	// TODO: Add your control notification handler code here
	if (bPlay == FALSE) {
		if (playback_timer == 1111)
			KillTimer(playback_timer);

		playback_timer = SetTimer(1111, 1000/fps, NULL);

		bPlay = TRUE;
		m_play_pause.SetWindowText("II");

		m_playbackA.StartPlay();
		m_playbackAA.StartPlay();
		m_playbackB.StartPlay();
	}
	else // bPlay == TRUE
	{
		if (playback_timer == 1111)
			KillTimer(playback_timer);

		bPlay = FALSE;
		m_play_pause.SetWindowText("GO");

		m_playbackA.PausePlay();
		m_playbackAA.PausePlay();
		m_playbackB.PausePlay();
	}
}

BOOL CYUVAnalyzerDlg::PreTranslateMessage(MSG* pMsg) 
{
	// TODO: Add your specialized code here and/or call the base class
	if (pMsg->message == WM_KEYDOWN)
	{
		if (pMsg->wParam == 39) // next
		{
			this->OnNext();
		}
		if (pMsg->wParam == 37) // prev
		{
			this->OnPrev();
		}
	}
	if (pMsg->message == WM_DROPFILES)
	{
		if (pMsg->hwnd == m_playbackA) {
			char sPathName[1024];
			DragQueryFile(HDROP(pMsg->wParam), 0, sPathName, 128);
			m_playbackA.SetPathName(sPathName);
			m_playbackAA.SetPathName(sPathName);
			CFile *pFile = new CFile();
			if(!pFile->Open(sPathName, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone )) 
			{
				//AfxMessageBox("Can't open input file");
				delete pFile;
				return 0;
			}
			iTotalFrameNumberA = pFile->GetLength() / (iWidth*iHeight*3/2);
			if (iTotalFrameNumberA > iTotalFrameNumberB)
				iMaxFrameNumber = iTotalFrameNumberA;
			iCurrFrameNumber = 0;
			pFile->Close();
			delete pFile;

			Restart();
		}
		if (pMsg->hwnd == m_playbackAA) {
			char sPathName[1024];
			DragQueryFile(HDROP(pMsg->wParam), 0, sPathName, 128);
			m_playbackA.SetPathName(sPathName);
			m_playbackAA.SetPathName(sPathName);
			CFile *pFile = new CFile();
			if(!pFile->Open(sPathName, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone )) 
			{
				//AfxMessageBox("Can't open input file");
				delete pFile;
				return 0;
			}
			iTotalFrameNumberA = pFile->GetLength() / (iWidth*iHeight*3/2);
			if (iTotalFrameNumberA > iTotalFrameNumberB)
				iMaxFrameNumber = iTotalFrameNumberA;
			iCurrFrameNumber = 0;
			pFile->Close();
			delete pFile;

			Restart();
		}
		if (pMsg->hwnd == m_playbackB) {
			char sPathName[1024];
			DragQueryFile(HDROP(pMsg->wParam), 0, sPathName, 128);
			m_playbackB.SetPathName(sPathName);
			CFile *pFile = new CFile();
			if(!pFile->Open(sPathName, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone )) 
			{
				//AfxMessageBox("Can't open input file");
				delete pFile;
				return 0;
			}
			iTotalFrameNumberB = pFile->GetLength() / (iWidth*iHeight*3/2);
			if (iTotalFrameNumberB > iTotalFrameNumberA)
				iMaxFrameNumber = iTotalFrameNumberB;
			iCurrFrameNumber = 0;
			pFile->Close();
			delete pFile;

			Restart();
		}
	}

	return cdxCSizingDialog::PreTranslateMessage(pMsg);
}

void CYUVAnalyzerDlg::OnTimer(UINT nIDEvent) 
{
	// TODO: Add your message handler code here and/or call default
	if (nIDEvent == playback_timer) {
		iCurrFrameNumber++;
		if (iCurrFrameNumber>=iMaxFrameNumber) iCurrFrameNumber = 0;

		char sFrameNumber[16];
		sprintf( sFrameNumber, "%d/%d", iCurrFrameNumber+1, iMaxFrameNumber );
		m_frame_num.SetWindowText(sFrameNumber);
		ShowPSNR();
		m_progress.SetPos(iCurrFrameNumber);

		if (iCurrFrameNumber<iTotalFrameNumberA) {
			m_playbackA.ContinuePlay();
			m_playbackAA.ContinuePlay();
		}
		if (iCurrFrameNumber<iTotalFrameNumberB)
			m_playbackB.ContinuePlay();
	}

	cdxCSizingDialog::OnTimer(nIDEvent);
}

void CYUVAnalyzerDlg::OnSetting() 
{
	// TODO: Add your control notification handler code here
	CSettingDlg set_dlg;

	int nResponse = set_dlg.DoModal();
	if (nResponse == IDOK)
	{
		// TODO: Place code here to handle when the dialog is
		//  dismissed with OK
		fps = set_dlg.m_fps;
		iWidth = set_dlg.m_width;
		iHeight = set_dlg.m_height;

		Restart();
	}
	else if (nResponse == IDCANCEL)
	{
		// TODO: Place code here to handle when the dialog is
		//  dismissed with Cancel
	}
}

void CYUVAnalyzerDlg::Restart()
{
	if (playback_timer == 1111)
		KillTimer(playback_timer);
	bPlay = FALSE;
	m_play_pause.SetWindowText("GO");

	m_playbackA.SetYUVSize(iWidth, iHeight);
	m_playbackAA.SetYUVSize(iWidth, iHeight);
	m_playbackB.SetYUVSize(iWidth, iHeight);
	iTotalFrameNumberA = m_playbackA.ReStart();
	iTotalFrameNumberA = m_playbackAA.ReStart();
	iTotalFrameNumberB = m_playbackB.ReStart();
	if (iTotalFrameNumberB > iTotalFrameNumberA)
		iMaxFrameNumber = iTotalFrameNumberB;
	else
		iMaxFrameNumber = iTotalFrameNumberA;
	iCurrFrameNumber = 0;

	char sFrameNumber[16];
	sprintf( sFrameNumber, "%d/%d", iCurrFrameNumber+1, iMaxFrameNumber );
	m_frame_num.SetWindowText(sFrameNumber);
	ShowPSNR();
	m_progress.SetRange(0, iMaxFrameNumber-1);
	iCurrFrameNumber = 0;
	m_progress.SetPos(iCurrFrameNumber);

	Invalidate(TRUE);
}

void CYUVAnalyzerDlg::OnMV() 
{
	// TODO: Add your control notification handler code here
	UpdateData(TRUE);
	m_playbackA.SetShowMV( m_bMV.GetState()&0x0003 );
	m_playbackAA.SetShowMV( m_bMV.GetState()&0x0003 );
	m_playbackB.SetShowMV( m_bMV.GetState()&0x0003 );
	Invalidate(TRUE);
}

void CYUVAnalyzerDlg::OnOrigSize() 
{
	// TODO: Add your control notification handler code here
	if (bDual == FALSE) {
		MoveWindow(0, 0, 352+48, 288+103);
	} else {
		MoveWindow(0, 0, 352*2+88, 288+103);
	}
	CenterWindow();
}

float CYUVAnalyzerDlg::CalcPSNR(unsigned char *A, unsigned char *B, int yuv)
{
	float psnr;
	int	x, y, idx;
	long sum;

	sum = 0;
	if (yuv == YUV || yuv == YY) {
		for (y=0; y<iHeight; y++) {
			for (x=0; x<iWidth; x++) {
				idx = y*iWidth + x;
				sum += (*(A+idx) - *(B+idx)) * (*(A+idx) - *(B+idx));
			}
		}
		psnr = (float)( 10 * log10( (255*255) / (sum/(iHeight*iWidth)) ) );
	}
	if (yuv == UU) {
		for (y=0; y<iHeight/2; y++) {
			for (x=0; x<iWidth/2; x++) {
				idx = y*iWidth/2 + x;
				sum += (*(A+idx) - *(B+idx)) * (*(A+idx) - *(B+idx));
			}
		}
		psnr = (float)( 10 * log10( (255*255) / (sum/(iHeight/2*iWidth/2)) ) );
	}
	if (yuv == VV) {
		for (y=0; y<iHeight/2; y++) {
			for (x=0; x<iWidth/2; x++) {
				idx = y*iWidth/2 + x;
				sum += (*(A+idx) - *(B+idx)) * (*(A+idx) - *(B+idx));
			}
		}
		psnr = (float)( 10 * log10( (255*255) / (sum/(iHeight/2*iWidth/2)) ) );
	}

	return psnr;
}

void CYUVAnalyzerDlg::OnYUV() 
{
	// TODO: Add your control notification handler code here
	if ( bYUV == YUV ) {
		bYUV = YY;
		m_YUV.SetWindowText("Y");
	} else if ( bYUV == YY ) {
		bYUV = UU;
		m_YUV.SetWindowText("U");
	} else if ( bYUV == UU ) {
		bYUV = VV;
		m_YUV.SetWindowText("V");
	} else if ( bYUV == VV ) {
		bYUV = YUV;
		m_YUV.SetWindowText("YUV");
	}
	m_playbackA.SetColorful( bYUV );
	m_playbackAA.SetColorful( bYUV );
	m_playbackB.SetColorful( bYUV );
	ShowPSNR();
	Invalidate(TRUE);
}

void CYUVAnalyzerDlg::OnGrid() 
{
	// TODO: Add your control notification handler code here
	UpdateData(TRUE);
	m_playbackA.SetShowGrid( m_grid.GetState()&0x0003 );
	m_playbackAA.SetShowGrid( m_grid.GetState()&0x0003 );
	m_playbackB.SetShowGrid( m_grid.GetState()&0x0003 );
	Invalidate(TRUE);
}
